package cn.ac.ict.fpevaluation.fusion;

import java.util.*;

import cn.ac.ict.fpevaluation.bean.APBean;
import cn.ac.ict.fpevaluation.cluster.DataOperationToConsoleUtil;
import cn.ac.ict.fpevaluation.fastdtw.dtw.TimeWarpInfo;
import cn.ac.ict.fpevaluation.fastdtw.dtw.WarpPath;
import cn.ac.ict.fpevaluation.fastdtw.test.FastDTWTest;
import cn.ac.ict.fpevaluation.fastdtw.test.MagneticSample;

import cn.ac.ict.fpevaluation.bean.AtomTrajectoryBean;
import cn.ac.ict.fpevaluation.bean.BeanUtils;
import cn.ac.ict.fpevaluation.bean.FusionBean;
import cn.ac.ict.fpevaluation.utils.DataOperationToDiskUtils;

/**
 * 将同一条路径的多条轨迹进行融合,目的在于增强系统鲁棒性和针对不同用户和手机的轨迹可区分度.
 */
public class Fusion {



    /**
     * @return
     */
    public static List<FusionBean> fusion() {

        System.out.println("开始融合轨迹");
        List<FusionBean> fusionResult = null;
        try {
            List<List<AtomTrajectoryBean>> clusterResult = DataOperationToDiskUtils.readClusterResult();
            fusionResult = multiFusion(clusterResult);
            DataOperationToConsoleUtil.printFusionResult(fusionResult);
            DataOperationToDiskUtils.writeFusionResultToDisk(fusionResult);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return fusionResult;
    }

    /**
     * 融合多条路径
     *
     * @param data
     * @return
     */
    private static List<FusionBean> multiFusion(List<List<AtomTrajectoryBean>> data) {
        List<List<FusionBean>> fusionBeansList = new ArrayList<List<FusionBean>>();
        for (List<AtomTrajectoryBean> list : data) {
            List<FusionBean> temp = new ArrayList<FusionBean>();
            for (AtomTrajectoryBean bean : list) {
                temp.add(BeanUtils.atomTrajectoryBeanToFusionBean(bean));
            }
            fusionBeansList.add(temp);
        }


        List<FusionBean> res = new ArrayList<FusionBean>();

        for (List<FusionBean> list : fusionBeansList) {
            res.add(divide(list, 0, list.size() - 1));
        }

        return res;
    }

    /**
     * @param beans
     * @param begin
     * @param end
     * @return
     */
    private static FusionBean divide(List<FusionBean> beans, int begin, int end) {
        if (begin == end) return beans.get(begin);
        else if (begin + 1 == end) return merge(beans.get(begin), beans.get(end));
        else {
            int mid = begin + ((end - begin) >> 1);
            FusionBean left = divide(beans, begin, mid);
            FusionBean right = divide(beans, mid + 1, end);

            return merge(left, right);
        }
    }

    /**
     * 将两条轨迹合并为一条
     *
     * @param fusionBean1
     * @param fusionBean2
     * @return
     */
    private static FusionBean merge(FusionBean fusionBean1, FusionBean fusionBean2) {

        ArrayList<MagneticSample> samples1 = BeanUtils.fusionBeanToSample(fusionBean1);
        ArrayList<MagneticSample> samples2 = BeanUtils.fusionBeanToSample(fusionBean2);

        //FDTW
        TimeWarpInfo t = new FastDTWTest().calDistance(FastDTWTest.gettsArray(samples2), FastDTWTest.gettsArray(samples1), 3);
        WarpPath path = t.getPath();

        List<MagneticSample> fusionSamples = new ArrayList<MagneticSample>();

        //
        for (int i = 0; i < samples1.size(); i++) {
            //取轨迹1中的样本为A, 那么轨迹2中与A对应的点集为A', sample.add(A, A')
            List<MagneticSample> samples = new ArrayList<MagneticSample>();
            //添加A
            samples.add(samples1.get(i));

            //得到A'在轨迹2中的索引集合
            List<Object> list = path.getMatchingIndexesForI(i);
            //将A'添加进去
            for (Object o : list) {
                samples.add(samples2.get((int) o));
            }

            //将A与A'中的点融合
            fusionSamples.add(fusionSample(samples));
        }

        FusionBean res = new FusionBean();

        List<Double> xMagnetic = new ArrayList<Double>();
        List<Double> yMagnetic = new ArrayList<Double>();
        List<Double> zMagnetic = new ArrayList<Double>();
        for (MagneticSample sample : fusionSamples) {
            xMagnetic.add(sample.getMagXOrigin());
            yMagnetic.add(sample.getMagYOrigin());
            zMagnetic.add(sample.getMagZOrigin());
        }

        res.setxMagnetic(xMagnetic);
        res.setyMagnetic(yMagnetic);
        res.setzMagnetic(zMagnetic);
        //如果数量级足够大,那么🕖则为路径的中心
        res.setStartX((fusionBean1.getStartX() + fusionBean2.getStartX()) / 2);
        res.setStartY((fusionBean1.getStartY() + fusionBean2.getStartY()) / 2);
        res.setOrientation(fusionOritation(fusionBean1.getOrientation(), fusionBean2.getOrientation()));

        return res;
    }

    /**
     * 将轨迹的方向进行合并
     *
     * @param oritations1
     * @param oritations2
     * @return
     */
    private static List<Double> fusionOritation(List<Double> oritations1, List<Double> oritations2) {
        int i = 0, size1 = oritations1.size(), size2 = oritations2.size();
        List<Double> res = new ArrayList<Double>();
        List<Double> temp = new ArrayList<Double>();
        while (i < size1 && i < size2) {
            temp.add((oritations1.get(i) + oritations2.get(i)) / 2);
            i++;
        }

        if (i == size1) {
            while (i < size2) res.add(oritations2.get(i));
        }

        if (i == size2) {
            while (i < size1) res.add(oritations1.get(i));
        }

        res.addAll(temp);

        return res;
    }

    /**
     * 融合轨迹中的点
     *
     * @param samples
     * @return
     */
    private static MagneticSample fusionSample(List<MagneticSample> samples) {
        double xMagnetic = 0, yMagnetic = 0, zMagnetic = 0;
        for (MagneticSample sample : samples) {
            xMagnetic += sample.getMagXOrigin();
            yMagnetic += sample.getMagYOrigin();
            zMagnetic += sample.getMagZOrigin();
        }

        MagneticSample res = new MagneticSample();
        res.setMagXOrigin(xMagnetic / samples.size());
        res.setMagYOrigin(yMagnetic / samples.size());
        res.setMagZOrigin(zMagnetic / samples.size());

        return res;
    }

	/*
     * @author JML ����·�����
	 
	public static double calLineWidth(ArrayList<Double> data) {
		Collections.sort(data);

		double midValue = 0;// ��ֵ
		double min;// ��Сֵ
		double max;// ���ֵ
		int n = data.size();
		if (n % 2 == 0)
			midValue = (data.get(n / 2 - 1) + data.get((n + 2) / 2 - 1)) / 2.0;
		else
			midValue = data.get((n + 1) / 2 - 1);
		int half80Per = (int) (n * 0.4);// ȡ80%�����ݼ���·�����
		min = data.get(n / 2 - half80Per);
		max = data.get(n / 2 + half80Per);

		return max - min == 0 ? 1 : max - min;

	}*/

}
